package com.kintreda.common.oauth.jwt;

import com.kintreda.common.config.enums.UserTypeEnum;
import com.kintreda.common.config.exception.CommonBaseErrorCode;
import com.kintreda.common.config.exception.CommonBaseException;
import com.kintreda.common.mybatis.entity.SysAdmin;
import com.kintreda.common.mybatis.entity.SysUser;
import com.kintreda.common.oauth.security.service.UserService;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;



@Slf4j
@Component
public class JwtUtils {

    @Autowired
    private UserService userService;

    private static JwtUtils jwtUtils;

    @PostConstruct
    public void init(){
        jwtUtils.jwtUtils = this;
        jwtUtils.userService = userService;
    }

    /**
     * 创建Token
     *
     * @param userId
     * @param userType
     * @return
     */
    public static String createToken(String userId, String userType) {
        Date newDate = new Date();
        Date date = new Date(System.currentTimeMillis() + JwtConfig.EXPIRE_TIME);

        Map<String, Object> stringObjectMap = new LinkedHashMap<>();
        stringObjectMap.put("userId", userId);
        stringObjectMap.put("userType", userType);

        byte[] keyBytes = JwtConfig.SECRET.getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
                .setClaims(stringObjectMap)
                .setIssuedAt(newDate)
                .setExpiration(date)
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }

    /**
     * 创建Token自定义有效期
     * @param userId
     * @param userType
     * @param expire_time 有效期（单位：小时）
     * @return
     */
    public static String createToken(String userId, String userType,Long expire_time) {
        Date newDate = new Date();
        Date date = new Date(System.currentTimeMillis() + (expire_time * 3600 * 1000));

        Map<String, Object> stringObjectMap = new LinkedHashMap<>();
        stringObjectMap.put("userId", userId);
        stringObjectMap.put("userType", userType);

        byte[] keyBytes = JwtConfig.SECRET.getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
                .setClaims(stringObjectMap)
                .setIssuedAt(newDate)
                .setExpiration(date)
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();
    }


    /**
     * 获取网络请求的token
     *
     * @param httpServletRequest
     * @return
     */
    public static String getUserToken(HttpServletRequest httpServletRequest) {
        return httpServletRequest.getHeader(JwtConfig.HEADER);
    }

    /**
     * 验证
     *
     * @param token 用户的token
     * @return
     */
    public static boolean verification(String token) {
        Claims claims = getClaimsFromToken(token);
        String userType = claims.get("userType").toString();
        String userId = claims.get("userId").toString();
        if (StringUtils.isBlank(userId) || StringUtils.isBlank(userType)){
            return false;
        }
        Date now = new Date(System.currentTimeMillis());
        Date theExpirationTime = getTheExpirationTime(token);
        return now.before(theExpirationTime);
    }


    /**
     * 获取token的到期时间
     *
     * @param token 用户的token
     * @return 到期时间
     */
    public static Date getTheExpirationTime(String token) {
        return getClaimsFromToken(token).getExpiration();
    }

    /**
     * 通过网络请求的token直接返回当前有效的操作者id
     *
     * @param httpServletRequest
     * @return
     */
    public static int getUserId(HttpServletRequest httpServletRequest) {
        String userToken = getUserToken(httpServletRequest);

        Claims claimsFromToken = getClaimsFromToken(userToken);
        String userId = claimsFromToken.get("userId", String.class);
        return Integer.valueOf(userId);
    }


    /**
     * 通过网络请求的token直接返回当前有效的操作者类型
     *
     * @param httpServletRequest
     * @return
     */
    public static String getUserType(HttpServletRequest httpServletRequest) {
        String userToken = getUserToken(httpServletRequest);

        Claims claimsFromToken = getClaimsFromToken(userToken);
        String userType = claimsFromToken.get("userType", String.class);
        return userType;
    }

    /**
     * 从token中获取claim
     *
     * @param token token
     * @return claim
     */
    public static Claims getClaimsFromToken(String token) {
        try {
            byte[] keyBytes = JwtConfig.SECRET.getBytes();
            SecretKey key = Keys.hmacShaKeyFor(keyBytes);
            return Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        }catch (ExpiredJwtException e){
            throw new CommonBaseException(CommonBaseErrorCode.LOGIN_OVERTIME);
        }catch (SignatureException e) {
            throw new CommonBaseException(CommonBaseErrorCode.TOKEN_FAIL);
        }catch (Exception e){
            throw new CommonBaseException(CommonBaseErrorCode.ILLEGAL_TOKEN);
        }
    }

    /**
     * 从网络请求中获取claim
     * @param request
     * @return
     */
    public static Claims getClaimsFromRequest(HttpServletRequest request) {
        String userToken = getUserToken(request);
        return getClaimsFromToken(userToken);
    }

    /**
     * 获取用户昵称
     * @param request
     * @return
     */
    public static String getUserNick(HttpServletRequest request) {
        String token = getUserToken(request);
        if (StringUtils.isNotBlank(token)){
            String userType = JwtUtils.getUserType(request);
            Integer userId = JwtUtils.getUserId(request);
            if (userType.equals(UserTypeEnum.后台管理员.getType())){
                SysAdmin admin = jwtUtils.userService.getCurrentUser(userType, userId.toString(), SysAdmin.class);
                return admin.getName();
            }
            if (userType.equals(UserTypeEnum.用户.getType())){
                SysUser user = jwtUtils.userService.getCurrentUser(userType, userId.toString(), SysUser.class);
                return user.getNickName();
            }
        }
        return "-";
    }


    /**
     * 获取当前登录的用户信息
     * @param request
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getCurrentUserInfo(HttpServletRequest request,Class<T> clazz) {
        String userType = getUserType(request);
        Integer userId = getUserId(request);
        return jwtUtils.userService.userInfo(userType,userId.toString(),clazz);
    }
}
